package com.example.shop.common.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 二维码生成
 * 提供
 * 1.支付二维码的生成
 * 2.物流快递条形码的生成
 */
public class GenerateCodeUtil {

    private BufferedImage image;
    private String codeContent;
    private BarcodeFormat format;

    private GenerateCodeUtil(){
    }

    private GenerateCodeUtil(String contents, int width, int height, BarcodeFormat barcodeFormat) throws WriterException {
        codeContent = contents;
        format = barcodeFormat;
        image = defaultGenerate(contents,width,height,barcodeFormat);
    }

    private BufferedImage defaultGenerate(String contents,int width,int height,BarcodeFormat barcodeFormat) throws WriterException {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        //指定MultiFormatWriter().encode的content参数的编码格式
        hints.put(EncodeHintType.CHARACTER_SET,"UTF-8");
        //容错级别 这里选用最高级H级别
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.MARGIN, 0);
        return generate(contents,width,height,barcodeFormat,hints);
    }

    /**
     * 将图片转换成字节数据
     * @return 返回一个图片的字节数组
     * @throws IOException 如果写入的过程中出错，则会报该错误
     */
    public byte[] toByteArray() throws IOException {
        //字节输出流，用于保存二维码的位矩阵数据，并转换成字节响应给前端
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        //将bufferedImage里的二维码数据写入到byteArrayOutputStream中
        ImageIO.write(image,"jpg",byteArrayOutputStream);
        byteArrayOutputStream.flush();
        return byteArrayOutputStream.toByteArray();
    }

    public String toStrBase64() throws IOException {
        String base64 = Base64.getEncoder().encodeToString(toByteArray());
        return base64;
    }

    /**
     * @return 返回当前图片的宽
     */
    public int getWidth(){
        return image.getWidth();
    }

    /**
     * @return 返回当前图片的高
     */
    public int getHeight(){
        return image.getHeight();
    }

    /**
     * 将码图片重新写入到一个新图片中
     * @param newImage 新图片
     * @param x 码图片写入的x位置
     * @param y 码图片写入的y位置
     * @return 返回新图片的画布Graphics2D
     */
    public Graphics2D writeCodeImage(BufferedImage newImage,int x,int y){
        Graphics2D g2d = newImage.createGraphics();
        //设置新图片的背景颜色
        g2d.setColor(Color.WHITE);
        //填充整个屏幕
        g2d.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
        //设置笔刷
        g2d.setColor(Color.BLACK);
        // 画码到新的面板
        g2d.drawImage(image, x, y, image.getWidth(), image.getHeight(), null);
        return g2d;
    }
    /**
     * 向图片顶部居中插入内容
     * @param contents 要插入的内容
     * @param font 字体
     * @param fontHigh 行高
     * @param color 字体颜色
     * @return 返回码生成器
     */
    public GenerateCodeUtil insertWordsToMidTop(String contents, Font font, int fontHigh, Color color){
        //如果内容不为空，则将其和码写入到新图片中
        if (StringUtils.isNotEmpty(contents)) {
            //创建一个新图片
            BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight()+fontHigh, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = writeCodeImage(newImage, 0, fontHigh);
            //将文字写入到新图片中
            g2d.setColor(color);
            g2d.setFont(font);
            //获取该文字的在该图片中所占的长度
            int strWidth = g2d.getFontMetrics().stringWidth(contents);
            //居中显示、行高居中
            g2d.drawString(contents,(newImage.getWidth() - strWidth) / 2,(fontHigh+font.getSize())/2);
            //释放该图片所使用的所有系统资源，即之后就不能再使用图片对象了
            g2d.dispose();
            //将数据刷新到图片中
            newImage.flush();
            image = newImage;
        }
        return this;
    }


    /**
     * 向图片底部居中插入内容
     * @param contents 要插入的内容
     * @param font 字体
     * @param fontHigh 行高
     * @param color 字体颜色
     * @return 返回码生成器
     */
    public GenerateCodeUtil insertWordsToMidBottom(String contents, Font font, int fontHigh, Color color){
        //如果内容不为空，则将其和码写入到新图片中
        if (StringUtils.isNotEmpty(contents)) {
            //创建一个新图片
            BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight()+fontHigh, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = writeCodeImage(newImage, 0, 0);
            //将文字写入到新图片中
            g2d.setColor(color);
            g2d.setFont(font);
            //获取该文字的在该图片中所占的长度
            int strWidth = g2d.getFontMetrics().stringWidth(contents);
            //居中显示、行高居中
            //注意：文字写入的位置，是文字底部，比如：image.getHeight()，则文字则是刚好于图片内切在一起
            g2d.drawString(contents,(newImage.getWidth() - strWidth) / 2,image.getHeight()+(fontHigh+font.getSize())/2);
            //释放该图片所使用的所有系统资源，即之后就不能再使用图片对象了
            g2d.dispose();
            //将数据刷新到图片中
            newImage.flush();
            image = newImage;
        }
        return this;
    }

    /**
     * 向图片顶部插入内容
     * @param contents 要插入的内容
     * @param font 字体
     * @param fontHigh 行高
     * @param color 字体颜色
     * @param leftPadding 距离左边的内边距，单位是像素
     * @return 返回码生成器
     */
    public GenerateCodeUtil insertWordsToTop(String contents, Font font, int fontHigh, Color color, int leftPadding){
        //如果内容不为空，则将其和码写入到新图片中
        if (StringUtils.isNotEmpty(contents)) {
            //创建一个新图片
            BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight()+fontHigh, BufferedImage.TYPE_INT_RGB);
            //将码图片写入
            Graphics2D g2d = writeCodeImage(newImage, 0, fontHigh);
            //将文字写入到新图片中
            g2d.setColor(color);
            g2d.setFont(font);
            g2d.drawString(contents,leftPadding,(fontHigh+font.getSize())/2);
            //释放该图片所使用的所有系统资源，即之后就不能再使用图片对象了
            g2d.dispose();
            //将数据刷新到图片中
            newImage.flush();
            image = newImage;
        }
        return this;
    }

    /**
     * 向图片底部插入内容
     * @param contents 要插入的内容
     * @param font 字体
     * @param fontHigh 行高
     * @param color 字体颜色
     * @param leftPadding 距离左边的内边距，单位是像素
     * @return 返回码生成器
     */
    public GenerateCodeUtil insertWordsToBottom(String contents, Font font, int fontHigh, Color color, int leftPadding){
        //如果内容不为空，则将其和码写入到新图片中
        if (StringUtils.isNotEmpty(contents)) {
            //创建一个新图片
            BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight()+fontHigh, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = writeCodeImage(newImage, 0, 0);
            //将文字写入到新图片中
            g2d.setColor(color);
            g2d.setFont(font);
            //居中显示、行高居中
            g2d.drawString(contents,leftPadding,image.getHeight()+(fontHigh+font.getSize())/2);
            //释放该图片所使用的所有系统资源，即之后就不能再使用图片对象了
            g2d.dispose();
            //将数据刷新到图片中
            newImage.flush();
            image = newImage;
        }
        return this;
    }






    /**
     * 根据content的内容生成指定大小的GenerateCode二维码生成器
     * @param contents 二维码的内容
     * @param width 二维码的宽度，单位是像素
     * @param height 二维码的高度，单位是像素
     * @return 返回GenerateCode二维码生成器
     */
    public static GenerateCodeUtil generateQRCode(String contents, int width, int height) throws WriterException {
        return new GenerateCodeUtil(contents,width,height, BarcodeFormat.QR_CODE);
    }

    /**
     * 根据content的内容生成指定大小的条形码
     * @param contents 条形码的内容
     * @param width 条形码的宽度，单位是像素
     * @param height 条形码的高度，单位是像素
     * @return 返回条形码图片的字节数组
     */
    public static GenerateCodeUtil generateEAN13BarCode(String contents, int width, int height) throws WriterException {
        //创建EAN13条形码生成器
        return new GenerateCodeUtil(contents,width,height, BarcodeFormat.EAN_13);
    }

    /**
     *
     * @param contents 码的内容
     * @param width 码的宽度，单位是像素
     * @param height 码的高度，单位是像素
     * @param format 生成的码的类型，如：二维码、条形码等
     * @param hints 指定content参数的编码格式(这里的编码格式并不是单单值UTF-8的这种编码，而是包括了其他的格式，如：纠错程度为25%的EC单词格式的编码)
     * @return 返回位矩阵的字节数组
     */
    public static BufferedImage generate(String contents, int width, int height, BarcodeFormat format, Map<EncodeHintType, Object> hints) throws WriterException {
        //根据content内容生成对应大小的二维码位矩阵
        BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, format,width,height,hints);
        //BitMatrix matrix, String format, Path file
//        Path path= FileSystems.getDefault().getPath("D://","Test/test.jpg");
//        MatrixToImageWriter.writeToPath(bitMatrix,"jpg",path);
        //将二维码的位矩阵(其实就是一串二进制数据)，可以写入到bufferedImage图片缓冲区中
        BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
        return bufferedImage;
    }

    public static void main(String[] args) throws Exception {
        GenerateCodeUtil generateCode = GenerateCodeUtil.generateEAN13BarCode("123456789012", 360, 480);
        generateCode.insertWordsToMidBottom("123456789012".trim().replace(""," "),new Font("微软雅黑", Font.PLAIN, 20),40,Color.BLACK);
        FileOptionUtil.write("D:\\abc.jpg",generateCode.toByteArray());
    }
}
